package com.architecture.core.dao;

import com.architecture.core.entity.BaseEntity;
import com.architecture.core.entity.ExtendCode;
import com.architecture.core.utils.ConfigUtil;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * User: aguang
 * Date: 13-1-21
 * Time: 下午2:32
 * To change this template use File | Settings | File Templates.
 */
@Repository("baseDao")
public class BaseDAO<E extends BaseEntity> extends AbstractDAO<E> {

    @Override
    public void init() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void destroy() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * @param e
     * @return 返回唯一的Serializable主键
     */
    public Serializable save(E e) {
        return this.getSessioin().save(e);
    }

    public List<Serializable> save(List<E> eList) {

        List<Serializable> sList = new ArrayList<Serializable>();
        for (E e : eList) {
            sList.add(this.save(e));
        }
        return sList;
    }

    public void update(E e) {

        this.getSessioin().update(e);
    }

    public void update(List<E> eList) {
        for (E e : eList) {
            this.update(e);
        }
    }

    public void delOfLogical(E e) {
        e.setDeleted(true);
        this.getSessioin().update(e);
    }

    public void delOfLogical(List<E> eList) {
        for (E e : eList) {
            e.setDeleted(true);
            this.getSessioin().update(e);
        }
    }

    public void delOfPhysical(E e) {
        this.getSessioin().delete(e);
    }

    public void delOfPhysical(List<E> eList) {
        for (E e : eList) {
            this.getSessioin().delete(e);
        }
    }

    public void delOfLogicalById(Class c, String id) {
        String delEntityName = c.getSimpleName();
        Query query = this.getSessioin().createQuery(ConfigUtil.getString(ConfigUtil.D_DEL_ENTITY_BY_ID).replace(":entity", delEntityName));
        query.setString("id", id);
        query.setTimestamp("deleteTime", new Date());
        query.executeUpdate();
    }

    public void delOfPhysicalByUsername(String username) {

    }

    public void delOfPhysicalById(String id) {

    }

    public E findUnique(Class<E> c, String id) {
        return (E) this.getSessioin().get(c, id);
    }

    public E findUnique(String querySql) {
        return this.findUnique(querySql, null);
    }

    public E findUnique(String querySql, Map<String, Object> valMap) {
        String newQuerySql = addDefaultSql(querySql, null);
        Query query = this.getSessioin().createQuery(newQuerySql);

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }
        return (E) query.uniqueResult();
    }

    public List<E> find(String querySql) {
        return this.find(querySql, null, 0, 0, null);
    }

    public List<E> find(String querySql, Map<String, Object> valMap) {
        return this.find(querySql, valMap, 0, 0, null);
    }

    public List<E> find(String querySql, Map<String, Object> valMap, int firstIndex) {
        return this.find(querySql, valMap, firstIndex, 0, null);
    }

    public List<E> find(String querySql, Map<String, Object> valMap, int firstIndex, int fetchSize) {
        return this.find(querySql, valMap, firstIndex, fetchSize, null);
    }

    public List<E> find(String querySql, String order) {
        return this.find(querySql, null, 0, 0, order);
    }

    public List<E> find(String querySql, int firstIndex, String order) {
        return this.find(querySql, null, firstIndex, 0, order);
    }

    public List<E> find(String querySql, Map<String, Object> valMap, String order) {
        return this.find(querySql, valMap, 0, 0, order);
    }

    public List<E> find(String querySql, int firstIndex, int fetchSize, String order) {
        return this.find(querySql, null, firstIndex, fetchSize, order);
    }

    public List<E> find(String querySql, Map<String, Object> valMap, int firstIndex, String order) {
        return this.find(querySql, valMap, firstIndex, 0, order);
    }

    public List<E> find(String querySql, int firstIndex) {
        return this.find(querySql, null, firstIndex, 0, null);
    }

    public List<E> find(String querySql, int firstIndex, int fetchSize) {
        return this.find(querySql, null, firstIndex, fetchSize, null);
    }

    public List<E> find(String querySql, Map<String, Object> valMap, int firstIndex, int fetchSize, String order) {

        String newQuerySql = addDefaultSql(querySql, order);

        Query query = this.getSessioin().createQuery(newQuerySql);

        if (ConfigUtil.getLong(ConfigUtil.WRONG_NUM) != firstIndex) {
            query.setFirstResult(firstIndex);
        }

        if (ConfigUtil.getLong(ConfigUtil.WRONG_NUM) != fetchSize) {
            query.setFetchSize(fetchSize);
        }

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }
        return query.list();
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql) {
        return this.find(codeList,querySql, null, 0, 0, null);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, Map<String, Object> valMap) {
        return this.find(codeList,querySql, valMap, 0, 0, null);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, Map<String, Object> valMap, int firstIndex) {
        return this.find(codeList,querySql, valMap, firstIndex, 0, null);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, Map<String, Object> valMap, int firstIndex, int fetchSize) {
        return this.find(codeList,querySql, valMap, firstIndex, fetchSize, null);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, String order) {
        return this.find(codeList,querySql, null, 0, 0, order);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, int firstIndex, String order) {
        return this.find(codeList,querySql, null, firstIndex, 0, order);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, Map<String, Object> valMap, String order) {
        return this.find(codeList,querySql, valMap, 0, 0, order);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, int firstIndex, int fetchSize, String order) {
        return this.find(codeList,querySql, null, firstIndex, fetchSize, order);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, Map<String, Object> valMap, int firstIndex, String order) {
        return this.find(codeList,querySql, valMap, firstIndex, 0, order);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, int firstIndex) {
        return this.find(codeList,querySql, null, firstIndex, 0, null);
    }

    public List<ExtendCode> find(List<Long> codeList,String querySql, int firstIndex, int fetchSize) {
        return this.find(codeList,querySql, null, firstIndex, fetchSize, null);
    }

    /**
     * 注意这里只能针对一张表进行权限判断
     *
     * @param codeList     过滤出的资源代码，用于查询结果
     * @param querySql
     * @param valMap
     * @param firstIndex
     * @param fetchSize
     * @param order
     * @return
     */
    public List<ExtendCode> find(List<Long> codeList, String querySql, Map<String, Object> valMap, int firstIndex, int fetchSize, String order) {

        String newQuerySql = addDefaultSql(querySql, order);

        Query query = this.getSessioin().createQuery(newQuerySql);

        if (ConfigUtil.getLong(ConfigUtil.WRONG_NUM) != firstIndex) {
            query.setFirstResult(firstIndex);
        }

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }

        Iterator<ExtendCode> entityIt = query.iterate();

        if (ConfigUtil.getLong(ConfigUtil.WRONG_NUM) != fetchSize) {
            int i = 0;
            List<ExtendCode> entityList = new ArrayList<ExtendCode>();
            while (entityIt.hasNext()) {
                ExtendCode entity = entityIt.next();
                if (fetchSize < i && codeList.contains(entity.getCode())) {
                    entityList.add(entity);
                } else {
                    return entityList;
                }
            }
            return new ArrayList<ExtendCode>();
        } else {
            return query.list();
        }
    }

    public List<Map<String, Object>> findMap(String querySql) {
        return this.findMap(querySql, null, 0, 0, null);
    }

    public List<Map<String, Object>> findMap(String querySql, Map<String, Object> valMap) {
        return this.findMap(querySql, valMap, 0, 0, null);
    }

    public List<Map<String, Object>> findMap(String querySql, Map<String, Object> valMap, int firstIndex) {
        return this.findMap(querySql, valMap, firstIndex, 0, null);
    }

    public List<Map<String, Object>> findMap(String querySql, Map<String, Object> valMap, int firstIndex, int fetchSize) {
        return this.findMap(querySql, valMap, firstIndex, fetchSize, null);
    }

    public List<Map<String, Object>> findMap(String querySql, String order) {
        return this.findMap(querySql, null, 0, 0, order);
    }

    public List<Map<String, Object>> findMap(String querySql, Map<String, Object> valMap, String order) {
        return this.findMap(querySql, valMap, 0, 0, order);
    }

    public List<Map<String, Object>> findMap(String querySql, Map<String, Object> valMap, int firstIndex, String order) {
        return this.findMap(querySql, valMap, firstIndex, 0, order);
    }

    public List<Map<String, Object>> findMap(String querySql, int firstIndex) {
        return this.findMap(querySql, null, firstIndex, 0, null);
    }

    public List<Map<String, Object>> findMap(String querySql, int firstIndex, String order) {
        return this.findMap(querySql, null, firstIndex, 0, order);
    }

    public List<Map<String, Object>> findMap(String querySql, int firstIndex, int fetchSize) {
        return this.findMap(querySql, null, firstIndex, fetchSize, null);
    }

    public List<Map<String, Object>> findMap(String querySql, int firstIndex, int fetchSize, String order) {
        return this.findMap(querySql, null, firstIndex, fetchSize, order);
    }

    public List<Map<String, Object>> findMap(String querySql, Map<String, Object> valMap, int fisrtIndex, int fetchSize, String order) {

        String newQuerySql = addDefaultSql(querySql, order);

        Query query = this.getSessioin().createQuery(newQuerySql);

        if (ConfigUtil.getLong(ConfigUtil.WRONG_NUM) != fisrtIndex) {
            query.setFirstResult(fisrtIndex);
        }

        if (ConfigUtil.getLong(ConfigUtil.WRONG_NUM) != fetchSize) {
            query.setFetchSize(fetchSize);
        }

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }

        //存储查询数据，每个Map代表一行数据
        List<Map<String, Object>> allList = new ArrayList<Map<String, Object>>();

        //取得每列的列名
        List<String> paramList = this.decomposteParams(query.getQueryString().toString());

        int length = paramList.size();

        Iterator it = query.iterate();

        while (it.hasNext()) {
            Object[] objs = null;
            Object obj = it.next();

            if (obj instanceof Object[]) {
                objs = (Object[]) obj;
            } else {
                objs = new Object[]{obj};
            }

            //代表每行数据
            Map<String, Object> rowMap = new HashMap<String, Object>();

            for (int i = 0; i < length; i++) {
                rowMap.put(paramList.get(i), objs[i]);

            }

            allList.add(rowMap);

        }

        return allList;
    }

    public int count(String querySql) {
        return ((Integer) this.getSessioin().createQuery(querySql).iterate().next()).intValue();
    }

    public int count(String querySql, Map<String, Object> valMap) {
        Query query = this.getSessioin().createQuery(querySql);
        this.setQueryValue(query, valMap);
        return ((Integer) query.iterate().next()).intValue();
    }

    /**
     * 将对象变为托管对象
     *
     * @param e
     */
    public void merge(E e) {
        this.getSessioin().merge(e);
    }

    public void merge(List<E> eList) {

        for (E e : eList) {
            this.merge(e);
        }
    }

    /**
     * 从数据库中获取最新对象
     *
     * @param e
     */
    public void refresh(E e) {
        this.getSessioin().refresh(e);
    }

    public void refresh(List<E> eList) {
        for (E e : eList) {
            this.getSessioin().refresh(e);
        }
    }

    public int executeHQL(String hql) {
        return this.executeHQL(hql, null);
    }

    public int executeHQL(List<String> sqls) {

        int executeNum = 0;

        for (String sql : sqls) {
            executeNum += this.executeHQL(sql, null);
        }

        return executeNum;
    }

    public int executeHQL(List<String> hqls, List<Map<String, Object>> valMapList) {

        int executeNum = 0;

        int length = hqls.size();
        for (int i = 0; i < length; i++) {
            if (valMapList.get(i).size() > 0) {
                executeNum += this.executeHQL(hqls.get(i), valMapList.get(i));
            } else {
                executeNum += this.executeHQL(hqls.get(i));
            }
        }

        return executeNum;
    }

    public int executeHQL(String hql, Map<String, Object> valMap) {
        Query query = super.getSessioin().createQuery(hql);

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }
        return query.executeUpdate();
    }

    public int executeSQL(String sql) {
        return this.executeSQL(sql, null);
    }

    public int executeSQL(List<String> sqls) {

        int executeNum = 0;

        for (String sql : sqls) {
            executeNum += this.executeSQL(sql, null);
        }

        return executeNum;
    }

    public int executeSQL(List<String> sqlList, List<Map<String, Object>> valMapList) {

        int executeNum = 0;

        int sqlLength = sqlList.size();
        for (int i = 0; i < sqlLength; i++) {
            if (valMapList.get(i).size() > 0) {
                executeNum += this.executeSQL(sqlList.get(i), valMapList.get(i));
            } else {
                executeNum += this.executeSQL(sqlList.get(i));
            }
        }

        return executeNum;
    }

    public int executeSQL(String sql, Map<String, Object> valMap) {
        Query query = super.getSessioin().createSQLQuery(sql);

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }
        return query.executeUpdate();
    }

    public List<Object> executeSQLQuery(String sql) {
        return this.executeSQLQuery(sql, null);
    }

    public List<Object> executeSQLQuery(String sql, Map<String, Object> valMap) {

        Query query = super.getSessioin().createSQLQuery(sql);

        if (!CollectionUtils.isEmpty(valMap)) {
            this.setQueryValue(query, valMap);
        }

        return query.list();
    }

    public List<List<Object>> executeSQLQuery(List<String> sqls) {

        List<List<Object>> result = new ArrayList<List<Object>>();

        for (String sql : sqls) {
            result.add(this.executeSQLQuery(sql, null));
        }

        return result;
    }

    public List<List<Object>> executeSQLQuery(List<String> sqls, List<Map<String, Object>> valMapList) {
        List<List<Object>> result = new ArrayList<List<Object>>();

        int length = sqls.size();
        for (int i = 0; i < length; i++) {
            result.add(this.executeSQLQuery(sqls.get(i), valMapList.get(i)));
        }

        return result;
    }


    private void setQueryValue(Query query, Map<String, Object> valMap) {
        Set<String> keySet = valMap.keySet();
        for (String key : keySet) {
            Object value = valMap.get(key);
            if (value instanceof Byte) {
                query.setByte(key, (Byte) value);
            } else if (value instanceof Short) {
                query.setShort(key, (Short) value);
            } else if (value instanceof Integer) {
                query.setInteger(key, (Integer) value);
            } else if (value instanceof Long) {
                query.setLong(key, (Long) value);
            } else if (value instanceof Float) {
                query.setFloat(key, (Float) value);
            } else if (value instanceof Double) {
                query.setDouble(key, (Double) value);
            } else if (value instanceof Date) {
                query.setTimestamp(key, (Timestamp) value);
            } else if (value instanceof Boolean) {
                query.setBoolean(key, (Boolean) value);
            } else if (value instanceof Character) {
                query.setCharacter(key, (Character) value);
            } else {
                query.setString(key, value.toString());
            }
        }
    }

    /**
     * 向sql语句添加默认查询条件
     *
     * @param querySql
     * @param order
     * @return
     */
    private String addDefaultSql(String querySql, String order) {
        if (StringUtils.isBlank(order) && querySql.toLowerCase().contains("where") && querySql.toLowerCase().contains("result") && !querySql.toLowerCase().contains("order by") && querySql.toLowerCase().contains("select")) {
            return querySql += " " + ConfigUtil.getString(ConfigUtil.D_DEFAULT_CONDITION);
        } else if (StringUtils.isBlank(order)) {
            return querySql;
        } else {
            return querySql += " " + order;
        }
    }

    /**
     * 解析查询语句中的查询字段的名称
     *
     * @param queryString
     * @return
     */
    private List<String> decomposteParams(String queryString) {

        //将查询语句保存一个副本，用于查找select和where的位置
        String copyQuery = queryString.toLowerCase();

        int startIndex = copyQuery.lastIndexOf("select") + 7;
        int endIndex = copyQuery.indexOf("from");
        String paramStr = queryString.substring(startIndex, endIndex);

        //解析查询的字段名称
        String[] names = paramStr.split(",");

        List<String> paramList = new ArrayList<String>();

        for (String name : names) {
            paramList.add(name.trim());
        }

        return paramList;
    }

}
